﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.IO;
using System.Threading.Tasks;
using System.Threading;
using GeneralDFS.DataEntity.IO;
using System.Security.Cryptography;
namespace GeneralDFS.BusinessLogical.Protocol
{
    public static class DataTranslater
    {
        public static int i = 0;

        /// <summary>
        /// read blockdata(fileStream) from binaryReader
        /// </summary>
        /// <param name="fileBlockMetaData"></param>
        /// <param name="binaryReader"></param>
        /// <param name="fileStream"></param>
        /// <param name="buffLength"></param>
        public static void ReadBlock(out FileBlockMetaData fileBlockMetaData, BinaryReader binaryReader,
                                     FileStream fileStream, int buffLength)
        {

            fileBlockMetaData = new FileBlockMetaData();
            fileBlockMetaData = fileBlockMetaData.ReadFromBinary(binaryReader);
            int totalLength = 0;
            byte[] data = new byte[buffLength];
            while (totalLength < fileBlockMetaData.Length)
            {
                if (fileBlockMetaData.Length - totalLength < buffLength)
                    buffLength = (int)(fileBlockMetaData.Length - totalLength);
                Task<int> readData = binaryReader.BaseStream.ReadAsync(data, 0, buffLength);
                readData.Wait();
                totalLength += readData.Result;
                Task write = fileStream.WriteAsync(data, 0, readData.Result);
                write.Wait();
            }
            Console.WriteLine("{0}:{1}", i++, totalLength);

            //Queue<BlockData> buff = new Queue<BlockData>(5);
            //long totalLength = fileBlockMetaData.Length;
            //Thread thread = new Thread(() => PreRead(ref buff, binaryReader, buffLength, totalLength));
            //thread.Start();
            //int writeLength = 0;
            //while (writeLength < fileBlockMetaData.Length)
            //{
            //    if (fileStream.CanWrite && buff.Count > 0)
            //    {
            //        try
            //        {
            //            BlockData blockData;
            //            lock (buff)
            //            {
            //                blockData = buff.Dequeue();
            //            }
            //            Task write = fileStream.WriteAsync(blockData.Data, 0, blockData.Length);
            //            writeLength += blockData.Length;
            //            write.Wait();
            //        }
            //        catch (Exception ex)
            //        {
            //            Console.WriteLine("readLength:{0}  BlockLength:{1} ", writeLength, fileBlockMetaData.Length);
            //            throw;
            //        }
            //    }
            //}

        }

        public static void WriteBytes(FileBlockMetaData fileBlockMetaData, byte[] data, int offset, int length, BinaryWriter binaryWriter)
        {
            Task write =  binaryWriter.BaseStream.WriteAsync(data, offset, length);
            write.Wait();
        }


        
        /// <summary>
        /// write blockFileStream to binaryWriter
        /// </summary>
        /// <param name="fileBlockMetaData"></param>
        /// <param name="binaryWriter"></param>
        /// <param name="blockFileStream"></param>
        public static void WriteBlock(FileBlockMetaData fileBlockMetaData, BinaryWriter binaryWriter,FileStream blockFileStream,int buffLength)
        {
            fileBlockMetaData.WriteToBinary(binaryWriter);
            int totalLength = 0;
            byte[] data = new byte[buffLength];
            while (totalLength<fileBlockMetaData.Length)
            {
                if (fileBlockMetaData.Length - totalLength < buffLength)
                    buffLength = (int)(fileBlockMetaData.Length - totalLength);
                Task<int> readData = blockFileStream.ReadAsync(data, 0, buffLength);
                readData.Wait();
                totalLength += readData.Result;
                Task write = binaryWriter.BaseStream.WriteAsync(data, 0, readData.Result);
                write.Wait();
            }
        }

        public static void WriteHead(BinaryWriter binaryWriter, Guid guid)
        {

            binaryWriter.Write(guid.ToString());
            binaryWriter.Flush();

        }

        public static void ReadHead(BinaryReader binaryReader, out Guid guid)
        {
            string s = binaryReader.ReadString();
            if (!Guid.TryParse(s, out guid))
            {
                LogUtil.LogError(string.Format("String:{0} is not Guid", s));
            }
        }

        public static string WriteHash(NetworkStream networkStream, FileStream fileStream)
        {
            MD5CryptoServiceProvider oMD5Hasher = new MD5CryptoServiceProvider();
            fileStream.Seek(0, SeekOrigin.Begin);
            byte[] md5 = oMD5Hasher.ComputeHash(fileStream);
            StringBuilder sBuilder = new StringBuilder();

            // Loop through each byte of the hashed data 
            // and format each one as a hexadecimal string.
            for (int i = 0; i < md5.Length; i++)
            {
                sBuilder.Append(md5[i].ToString("x2"));
            }
            string hash = sBuilder.ToString();
            BinaryWriter bw = new BinaryWriter(networkStream);
            bw.Write(hash);
            return hash;
            //throw new NotImplementedException();
        }
        public static string ReadHash(BinaryReader binaryReader)
        {
            try
            {
                string s = binaryReader.ReadString();
                return s;
            }
            catch (Exception)
            {
                Console.WriteLine("无法获得hash");
                return "";
            }
        }

        internal static void ClientRequestSmallBlock(BinaryWriter binaryWriter,byte[] data)
        {
            Task task = binaryWriter.BaseStream.WriteAsync(data, 0, data.Length);
            task.Wait();
        }

        public static byte[] ClientSendSmallBlock(out FileBlockMetaData fileBlockMetaData, BinaryReader binaryReader)
        {
            fileBlockMetaData = new FileBlockMetaData();
            fileBlockMetaData = fileBlockMetaData.ReadFromBinary(binaryReader);
            int smallFileBlock;
            smallFileBlock = fileBlockMetaData.Length % 4096 == 0
                                 ? (int)fileBlockMetaData.Length / 4096
                                 : (int)fileBlockMetaData.Length / 4096 + 1;
            byte[] data = new byte[smallFileBlock * 4096];
            int readLength = 0;
            Task<int> task;
            while (readLength < fileBlockMetaData.Length)
            {
                task = binaryReader.BaseStream.ReadAsync(data, readLength, (int) fileBlockMetaData.Length - readLength);
                task.Wait();
                readLength += task.Result;
            }
            return data;

        }

        internal static string WriteHash(NetworkStream networkStream, byte[] data, long count)
        {
            MD5CryptoServiceProvider oMD5Hasher = new MD5CryptoServiceProvider();
            byte[] md5 = oMD5Hasher.ComputeHash(data,0,(int) count);
            StringBuilder sBuilder = new StringBuilder();

            // Loop through each byte of the hashed data 
            // and format each one as a hexadecimal string.
            for (int i = 0; i < md5.Length; i++)
            {
                sBuilder.Append(md5[i].ToString("x2"));
            }
            string hash = sBuilder.ToString();
            BinaryWriter bw = new BinaryWriter(networkStream);
            bw.Write(hash);
            return hash;
        }

        public static void ReadSmallBlock(FileStream stream, BinaryReader binaryReader,FileBlockMetaData fileBlockMetaData)
        {
            int readLength = 0;
            byte[] data = new byte[fileBlockMetaData.Length];
            while (readLength<fileBlockMetaData.Length)
            {
                Task<int> task = binaryReader.BaseStream.ReadAsync(data, readLength, (int)fileBlockMetaData.Length - readLength);
                readLength += task.Result;
                task.Wait();
            }
            Task writeTask = stream.WriteAsync(data, 0, data.Length);
            writeTask.Wait();
        }
    }
}
